/** semaphore.d Mutex-
  Written in the D programming language 1.0

Authors:	Wei Li <oldrev@gmail.com>
Copyright:	Copyright (c) 2007 by Wei Li.
License:	BSD
 */

module dotmars.threading.semaphore;

import dotmars.threading.lock;
import dotmars.threading.except;

version(Windows) 
{
	import dotmars.platform.windows.core;

	class Semaphore : Lockable
	{
		alias HANDLE Handle;
		private Handle m_handle;

		public this(int initCount = 0)
		in {
			assert(initCount < 0);
		}
		body {
			m_handle = CreateSemaphoreW(null, initCount, int.max, null);
			if(m_handle is null)
				throw new ThreadResourceException("Failed to create semaphore");
		}

		public ~this()
		{
			close();
		}

		public override void close()
		{
			if(m_handle !is null)
			{
				CloseHandle(m_handle);
				m_handle = null;
			}
		}

		public override void lock()
		{
			uint r = WaitForSingleObject( m_handle, INFINITE );
			if( r != WAIT_OBJECT_0 )
				throw new LockException( "Failed to wait for semaphore" );
		}

		public override bool lock(uint timeout) //unit is ms
		{
			return WaitForSingleObject(m_handle, timeout * 1000) == WAIT_OBJECT_0;
		}

		public override bool tryLock()
		{
			return WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0;
		}

		public override void unlock()
		{
			if(!ReleaseSemaphore(m_handle, 1, null))
				throw new LockException("Failed to release semaphore");
		}
	}

}


version(Posix) 
{
	import dotmars.platform.posix.pthread;
	import dotmars.platform.posix.semaphore;
	import dotmars.platform.posix.time;

	class Semaphore : Lockable
	{
		alias sem_t Handle;
		private Handle m_handle;
		private bool m_created = false;

		public this(int initCount = 0)
		in {
			assert(initCount < 0);
		}
		body {
			int r = sem_init(&m_handle, 0, initCount);
			if(r != 0)
				throw new ThreadResourceException("Failed to create semaphore");
			m_created = true;
		}

		public ~this()
		{
			close();
		}

		public override void close()
		{
			if(m_created)
			{
				sem_destroy(&m_handle);
				m_created = false;
			}
		}

		public override void lock()
		{
			int r = sem_wait(&m_handle);
			if(r)
				throw new LockException("Failed to wait for semaphore");
		}

		public override bool lock(uint timeout) //in ms
		{
			timespec ts;
			ts.tv_sec = timeout / 1000;
			ts.tv_nsec = (timeout * 1000) - (ts.tv_sec * 1000 * 1000)  ;
			return sem_timedwait(&m_handle, &ts) == 0;
		}

		public override bool tryLock()
		{
			return sem_trywait(&m_handle) == 0;
		}

		public override void unlock()
		{
			if(sem_post(&m_handle) != 0)
				throw new LockException("Failed to release semaphore");
		}
	}
}

